#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

#include "cs104_slave.h"

#include "hal_thread.h"
#include "hal_time.h"

static bool running = true;

void
sigint_handler(int signalId)
{
    running = false;
}

void
printCP56Time2a(CP56Time2a time)
{
    printf("%02i:%02i:%02i %02i/%02i/%04i", CP56Time2a_getHour(time),
                             CP56Time2a_getMinute(time),
                             CP56Time2a_getSecond(time),
                             CP56Time2a_getDayOfMonth(time),
                             CP56Time2a_getMonth(time),
                             CP56Time2a_getYear(time) + 2000);
}

/* Callback handler to log sent or received messages (optional) */
static void
rawMessageHandler(void* parameter, IMasterConnection conneciton, uint8_t* msg, int msgSize, bool sent)
{
    if (sent)
        printf("SEND: ");
    else
        printf("RCVD: ");

    int i;
    for (i = 0; i < msgSize; i++) {
        printf("%02X ", msg[i]);
    }

    printf("\n");
}

static bool
clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime)
{
    printf("Process time sync command with time "); printCP56Time2a(newTime); printf("\n");

    uint64_t newSystemTimeInMs = CP56Time2a_toMsTimestamp(newTime);

    /* Set time for ACT_CON message */
    CP56Time2a_setFromMsTimestamp(newTime, Hal_getTimeInMs());

    /* update system time here */

    return true;
}
float randf() {
	int r = (rand() >> 8) | 0x3f800000;;
	return *(float *)&r - 1;
}
static bool
interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
{
    printf("Received interrogation for group %i\n", qoi);

    if (qoi == 20) { /* only handle station interrogation */

        CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);

        IMasterConnection_sendACT_CON(connection, asdu, false);

        /* The CS101 specification only allows information objects without timestamp in GI responses */

        // CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,
        //         0, 1, false, false);
        // //第一条数据创建的时候，io是为空的
        // InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 100, 100, IEC60870_QUALITY_GOOD);

        // CS101_ASDU_addInformationObject(newAsdu, io);
        // //后续的数据创建的时候，使用的是之前一步创建出来的io
        // CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
        //     MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD));

        // CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
        //     MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD));
        // //传输三相电压数据-在实际使用的时候要不断的去做点表的对应


        // InformationObject_destroy(io);
        // IMasterConnection_sendASDU(connection, newAsdu);
        // CS101_ASDU_destroy(newAsdu);
        //发送完一条报文，如果要发送下一条，需要摧毁当前的asdu和io
        //作为响应结束的一个方式

        CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,
                0, 1, false, false);
        //第一条数据创建的时候，io是为空的
        //MeasuredValueShort保存的是浮点数，该点表返回的是变电站内两个柜的电压值
        InformationObject io = (InformationObject) MeasuredValueShort_create(NULL, 0, 220.00, IEC60870_QUALITY_GOOD);
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 1, 222.01+randf(), IEC60870_QUALITY_GOOD));
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 2, 221.46+randf(), IEC60870_QUALITY_GOOD));
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 3, 219.85+randf(), IEC60870_QUALITY_GOOD)); 
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 4, 221.09+randf(), IEC60870_QUALITY_GOOD));
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 5, 281.49+randf(), IEC60870_QUALITY_GOOD));
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            MeasuredValueShort_create(NULL, 6, 195.93+randf(), IEC60870_QUALITY_GOOD)); 

        InformationObject_destroy(io);
        IMasterConnection_sendASDU(connection, newAsdu);
        CS101_ASDU_destroy(newAsdu);    

        //描述了两个柜断路器的开关的状态
        newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,
                        0, 1, false, false);
        io = SinglePointInformation_create(NULL, 0,true,IEC60870_QUALITY_GOOD);
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            SinglePointInformation_create(NULL, 1,true,IEC60870_QUALITY_GOOD));
        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
            SinglePointInformation_create(NULL, 2,false,IEC60870_QUALITY_GOOD));

        InformationObject_destroy(io);
        IMasterConnection_sendASDU(connection, newAsdu);
        CS101_ASDU_destroy(newAsdu);

        IMasterConnection_sendACT_TERM(connection, asdu);
    }
    else {
        IMasterConnection_sendACT_CON(connection, asdu, true);
    }

    return true;
}


/*
服务端处理客户端发来的请求
*/
static bool
asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu)
{
    if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) {
        printf("received single command\n");

        if  (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) {
            InformationObject io = CS101_ASDU_getElement(asdu, 0);

            if (io) {
                if (InformationObject_getObjectAddress(io) == 5000) {
                    SingleCommand sc = (SingleCommand) io;

                    printf("IOA: %i switch to %i\n", InformationObject_getObjectAddress(io),
                            SingleCommand_getState(sc));

                    CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
                }
                else
                    CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);

                InformationObject_destroy(io);
            }
            else {
                printf("ERROR: message has no valid information object\n");
                return true;
            }
        }
        else
            CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);

        IMasterConnection_sendASDU(connection, asdu);

        return true;
    //客户端向服务端传递整数
    }else if(CS101_ASDU_getTypeID(asdu) == C_SE_NB_1){
        //接收到数据后，保存再本地，（其他部分的代码拿去使用，在模拟的情况下，把客户端传来的数值打印输出即可）
        printf("received Setpoint command, scaled value\n");
        if  (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION){
            printf("CS101_COT_ACTIVATION\n");
            for(int i=0;i<=1;i++){
                InformationObject io = CS101_ASDU_getElement(asdu, i);
                SetpointCommandScaled sc = (SetpointCommandScaled) io;
                printf("IOA: %i value to %i action to %i\n", InformationObject_getObjectAddress(io),
                        SetpointCommandScaled_getValue(sc), 
                        SetpointCommandScaled_isSelect(sc)?1:0
                        );
                InformationObject_destroy(io);
            }
        
        /*
        对于客户端的写操作
        标准的服务端（模仿仿真器的写法），打印客户端传来的参数即可，不需要返回报文
        */
        }
    //客户端处理的是浮点数
    }else if(CS101_ASDU_getTypeID(asdu) == C_SE_NA_1){
        printf("Setpoint command, normalized value\n");
        if  (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION){
            printf("CS101_COT_ACTIVATION\n");
            for(int i=0;i<=1;i++){
                InformationObject io = CS101_ASDU_getElement(asdu, i);
                SetpointCommandNormalized sc = (SetpointCommandNormalized) io;
                printf("IOA: %i value to %f action to %i\n", InformationObject_getObjectAddress(io),
                        SetpointCommandNormalized_getValue(sc), 
                        SetpointCommandNormalized_isSelect(sc)?1:0
                        );
                InformationObject_destroy(io);
            }
        
        /*
        对于客户端的写操作
        标准的服务端（模仿仿真器的写法），打印客户端传来的参数即可，不需要返回报文
        */
        }
    }else if(CS101_ASDU_getTypeID(asdu) == C_SE_NC_1){
        printf("SetpointCommandShort\n");
        if  (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION){
            //printf("CS101_COT_ACTIVATION\n");
            for(int i=0;i<=1;i++){
                InformationObject io = CS101_ASDU_getElement(asdu, i);
                SetpointCommandShort sc = (SetpointCommandShort) io;
                printf("IOA: %i value to %f action to %i\n", InformationObject_getObjectAddress(io),
                        SetpointCommandShort_getValue(sc), 
                        SetpointCommandShort_isSelect(sc)?1:0
                        );
                InformationObject_destroy(io);
            }
        
        /*
        对于客户端的写操作
        标准的服务端（模仿仿真器的写法），打印客户端传来的参数即可，不需要返回报文
        */
        }
    }
    return false;
}

static bool
connectionRequestHandler(void* parameter, const char* ipAddress)
{
    printf("New connection request from %s\n", ipAddress);

#if 0
    if (strcmp(ipAddress, "127.0.0.1") == 0) {
        printf("Accept connection\n");
        return true;
    }
    else {
        printf("Deny connection\n");
        return false;
    }
#else
    return true;
#endif
}

static void
connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event)
{
    if (event == CS104_CON_EVENT_CONNECTION_OPENED) {
        printf("Connection opened (%p)\n", con);
    }
    else if (event == CS104_CON_EVENT_CONNECTION_CLOSED) {
        printf("Connection closed (%p)\n", con);
    }
    else if (event == CS104_CON_EVENT_ACTIVATED) {
        printf("Connection activated (%p)\n", con);
    }
    else if (event == CS104_CON_EVENT_DEACTIVATED) {
        printf("Connection deactivated (%p)\n", con);
    }
}

int
main(int argc, char** argv)
{
    /* Add Ctrl-C handler */
    signal(SIGINT, sigint_handler);

    /* create a new slave/server instance with default connection parameters and
     * default message queue size */
    CS104_Slave slave = CS104_Slave_create(10, 10);

    CS104_Slave_setLocalAddress(slave, "0.0.0.0");
    //CS104_Slave_setLocalPort(CS104_Slave self, int tcpPort)
    //IEC104服务端默认运行在2404端口
    CS104_Slave_setLocalPort(slave,2404);

    /* Set mode to a single redundancy group
     * NOTE: library has to be compiled with CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP enabled (=1)
     */
    CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);

    /* get the connection parameters - we need them to create correct ASDUs -
     * you can also modify the parameters here when default parameters are not to be used */
    CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave);

    /* when you have to tweak the APCI parameters (t0-t3, k, w) you can access them here */
    CS104_APCIParameters apciParams = CS104_Slave_getConnectionParameters(slave);

    printf("APCI parameters:\n");
    printf("  t0: %i\n", apciParams->t0);
    printf("  t1: %i\n", apciParams->t1);
    printf("  t2: %i\n", apciParams->t2);
    printf("  t3: %i\n", apciParams->t3);
    printf("  k: %i\n", apciParams->k);
    printf("  w: %i\n", apciParams->w);

    /* set the callback handler for the clock synchronization command */
    CS104_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL);

    /* set the callback handler for the interrogation command */
    CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL);

    /* set handler for other message types */
    CS104_Slave_setASDUHandler(slave, asduHandler, NULL);

    /* set handler to handle connection requests (optional) */
    CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL);

    /* set handler to track connection events (optional) */
    CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL);

    /* uncomment to log messages */
    CS104_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL);

    CS104_Slave_start(slave);

    if (CS104_Slave_isRunning(slave) == false) {
        printf("Starting server failed!\n");
        goto exit_program;
    }

    int16_t scaledValue = 0;

    while (running) {

        Thread_sleep(1000);

        // CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);
        // InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 110, scaledValue, IEC60870_QUALITY_GOOD);
        // scaledValue++;
        // CS101_ASDU_addInformationObject(newAsdu, io);
        // InformationObject_destroy(io);
        // /* Add ASDU to slave event queue */
        // CS104_Slave_enqueueASDU(slave, newAsdu);
        // CS101_ASDU_destroy(newAsdu);
    }

    CS104_Slave_stop(slave);

exit_program:
    CS104_Slave_destroy(slave);

    Thread_sleep(500);
}

// CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
// //IMasterConnection_sendACT_CON(connection, asdu, false);
// /* The CS101 specification only allows information objects without timestamp in GI responses */
// CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,
//         0, 1, false, false);
// //第一条数据创建的时候，io是为空的
// InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 100, -1, IEC60870_QUALITY_GOOD);
// CS101_ASDU_addInformationObject(newAsdu, io);
// //后续的数据创建的时候，使用的是之前一步创建出来的io
// CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
//     MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD));
// CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
//     MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD));
// InformationObject_destroy(io);
// IMasterConnection_sendASDU(connection, newAsdu);

//CS101_ASDU_destroy(newAsdu);
//发送完一条报文，如果要发送下一条，需要摧毁当前的asdu和io
//作为响应结束的一个方式
//IMasterConnection_sendACT_TERM(connection, asdu);